/*
 * Copyright (C) 2012 scchang <scchang@caltech.edu>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ogame.utilities.datastorage.subtables;

import com.almworks.sqlite4java.SQLiteConnection;
import com.almworks.sqlite4java.SQLiteException;
import com.almworks.sqlite4java.SQLiteStatement;
import ogame.utilities.Constants;
import ogame.utilities.Player;
import ogame.utilities.datastorage.OgameDataStorage;
import ogame.utilities.planets.Planet;
import ogame.utilities.planets.Planets;
import ogame.utilities.research.Research;
import ogame.utilities.research.ResearchType;

/**
 *
 * @author scchang <scchang@caltech.edu>
 */
public class PlayerTable {

    /**
     * Connection for current database.
     */
    protected SQLiteConnection db;
    
    /**
     * Connection interface for all the other tables.
     */
    protected OgameDataStorage ogameDataStorage;
    
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Constructs and initializes a new player table interface.
     * @param db, Already opened db
     */
    public PlayerTable(SQLiteConnection db, OgameDataStorage ogameDataStorage){
        this.ogameDataStorage = ogameDataStorage;
        this.db = db;
        this.initialzePlayerTable();
    }
    
    ///////////////////////////////////////////////////////////////////////////
    /**
     * Initializes the player table within the member database.
     */
    protected void initialzePlayerTable(){
        // Simple Inject
        String sql_inject = "CREATE TABLE IF NOT EXISTS Players (";
        for(int i = 0; i < Constants.DATA_STORAGE_PLAYER_COLS.length; ++i){
            sql_inject += " " + Constants.DATA_STORAGE_PLAYER_COLS[i];
            if(i < Constants.DATA_STORAGE_PLAYER_COLS.length - 1){
                sql_inject += ",";
            }
        }
        sql_inject += ")";
        
        try{
            this.db.exec(sql_inject);
        }catch(SQLiteException e){
            e.printStackTrace();
            System.out.println("Unable to create player table.");
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////
    /**
     * Determines if the given player is already in table. If so, an update is needed.
     * @param player, The player that could be in the table
     * @return integer of the p_id, -1 if unable to do so
     */
    public int getPlayerID(Player player){
        try{
            SQLiteStatement st = this.db.prepare("SELECT player_id FROM Players "
                    + "WHERE name =?");
            st.bind(1, player.getName());
            
            if(st.step()){
                int id = st.columnInt(0);
                st.dispose();
                return id;
            }
            st.dispose();
            return -1;
        }catch(SQLiteException e){
            e.printStackTrace();
            System.out.println("In getPlayerID: Unable to get player id.");
            return -1;
        }
    }
    
    
    ///////////////////////////////////////////////////////////////////////////
    /**
     * Updates table with new planet.  Will create player entry if it doesn't 
     * exist.
     * @param player, The player to be modified
     * @param planet, The planet to be modified
     * @return , Success of the operation.
     */
    public boolean updatePlayerWithNewPlanet(Player player, int planet_id){
        try{
            int playerID = this.getPlayerID(player);
        
            if(playerID != -1){
                // Player is already in table
                
                SQLiteStatement st = this.db.prepare("SELECT planets, research FROM Players WHERE player_id=?");
                st.bind(1, playerID);
                
                if(st.step()){
                    byte[] planets = st.columnBlob(0);
                    planets = Planets.addPlanetToList(planets, playerID);

                    // Determine if we need to update research
                    byte[] researchEncoded = st.columnBlob(1);
                    Research researchOld = Research.parseResearch(researchEncoded);
                    Research researchNew = player.getResearch();
                    boolean updateNeeded = false;
                    for(ResearchType type : ResearchType.values()){
                        if(researchOld.get(type) < researchNew.get(type)){
                            updateNeeded = true;
                            break;
                        }
                    }
                    
                    if(updateNeeded){
                        researchEncoded = researchNew.encode();
                    }
                    
                    st.dispose();
                    
                    st = this.db.prepare("UPDATE Players SET planets=?1, research=?2 WHERE player_id=?3");
                    st.bind(1, planets);
                    st.bind(2, researchEncoded);
                    st.bind(3, playerID);
                    
                    st.step();
                }
                st.dispose();
                return true;
            }else{
                // New player is needed
                int nextPlayerID = this.ogameDataStorage.getGeneralTable().getNextPlayerID();
                
                if(nextPlayerID == -1){
                    System.out.println("In updatePlayerWithNewPlanet, "
                            + "unable to get next player id.");
                    return false;
                }
                
                Planets planets = player.getPlanets();
                byte[] bytePlanets = new byte[1];
                bytePlanets[0] = Byte.parseByte("0");
                
                for(Planet planet : planets){
                    int planetID = this.ogameDataStorage.getPlanetTable().getPlanetID(planet);
                    if(planetID != -1){
                        bytePlanets = Planets.addPlanetToList(bytePlanets, planetID);
                    }
                }
                
                Research research = player.getResearch();
                byte[] byteResearch = research.encode();
                
                SQLiteStatement st = this.db.prepare("INSERT INTO Players VALUES "
                        + "(?1, ?2, ?3, ?4)");
                st.bind(1, nextPlayerID);
                st.bind(2, player.getName());
                st.bind(3, bytePlanets);
                st.bind(4, byteResearch);
                
                st.step();
                st.dispose();                
                if(!this.ogameDataStorage.getGeneralTable().incrementPlayerID()){
                    System.out.println("In updatePlayerWithNewPlanet, "
                            + "Unable to increment player id.");
                    return false;
                }
                return true;
            }
        }catch(SQLiteException e){
            e.printStackTrace();
            System.out.println("Unable to update player with new planet.");
            return false;
        }
        
    }
    
    ///////////////////////////////////////////////////////////////////////////
    /**
     * Method to get player from the input id.
     * @param id, The input id of the player requested
     * @return 
     */
    public Player getPlayerByID(int id){
        int count = 0;
        try{
            SQLiteStatement st = this.db.prepare("SELECT * FROM Players WHERE player_id=?1");
            st.bind(1, id);
            
            while(st.step()){
                count++;

                int player_id = st.columnInt(0);
                String player_name = st.columnString(1);
                byte planets[] = st.columnBlob(2);
                byte research[] = st.columnBlob(3);
                
                Research result_research = Research.parseResearch(research);
                Player player = new Player(player_name);
                
                player.setResearch(result_research);
                Planets result_planets = Planets.parsePlanets(planets, player, 
                        this.ogameDataStorage);
                st.dispose();
                return player;
            }
            st.dispose();
        }catch(SQLiteException e){
            System.out.println("In getPLayerByID: Unable to get player with id: " + id);
            e.printStackTrace();
        }

        return null;
    }
    
}
